En este corto taller vamos a aprender las herramientas básicas para visualizar su data. La visualización de datos, ampliamente entendida, es una manera (efectiva, cuando se la hace bien) de contar una historia (la historia que tú quieres contar). Una mala visualización de datos es como una mala narrativa. Una buena visualización de datos es chefs kiss.
A manera resumida, hay dos tipos de visualización de datos:
Tomando en cuenta estas dos categorías, es importante mantener en algún lado de su psiquis las siguientes características cuando estén desarrollando sus gráficos:
En el resto de este taller, vamos a utilizar R para que de tus números puedas sacar una historia (o los principios de una historia). Este taller no va a cubrir teorías sobre visualización de datos. Para eso necesitaríamos bastante más tiempo. Sin embargo, sí recomiendo que lean al respecto. Aunque se que igual no lo van a hacer, una buena introducción al tema es Wilde, Clause (2019) - Fundamentals of Data Visualization.
tidyverse, y
ggplot2En la primera parte vamos a ver diferentes formatos de data, cómo
cargarlos a R, entender la forma de sus bases de
datos, entender la forma que ggplot2 prefiere en
que estén sus datos, cómo crear variables, cómo modificar sus bases de
datos, y, finalmente, la intuición básica para crear gráficos en
R.
Para este taller vamos a utilizar, principalmente, dos paquetes de
R con los cuales deberían estar familiarizadxs:
tidyverse y ggplot2. tidyverse es
un paquete que nos ayuda a manipular nuestros datos (por ej.,
eliminar variables, agregar observaciones, unir bases de datos). Tiene
un lenguaje particular: pipes. Como este no es un taller de
tidyverse y ustedes son estudiantes del Tec y pagan una
millonada por su educación, voy a asumir que todxs tienen un
conocimiento básico de tidyverse. Si quieren repasar cómo
funciona este paquete, pueden revisar el siguiente enlace.
ggplot2 está incorporado a tidyverse, y es una
herramienta bastante desarrollada, intuitiva (cuando le agarras la
vuelta), y con una gramática consistente. En este taller vamos a
adentrarnos en la lógica de ggplot2. Sin embargo, no
lograremos cubrir absolutamente todo lo que se puede hacer con
ggplot2 en específico, ni con R en general.
Para leer más sobre la gramática de ggplot, recomiendo que
lean Wickham,
Hadlye (2010) - A Layered Grammar of Graphics. Para resolver sus
preguntas sobre ggplot2, recomiendo que exploren las
siguiente páginas:
¡Comencemos!
Voy a asumir que ya tienen una base de datos. Lo primero que van a
tener que hacer es importar esa base de datos a R. Las
bases de datos vienen en todo tipo de formas y colores, pero sobre todo,
en diferentes formatos. Nos vamos a enfocar en los siguientes
formatos:
R
para guardar información.
dataframes, al igual que
otros objetos creados en R. Es bueno para compactar la información, por
lo tanto utiliza menos espacio en el disco. Es el formato que
R lee más rápido.Otras extensiones:
Existen otras extensiones. Para casi todas hay un paquete en
R y una rápida búsqueda en el internet les dará la
respuesta.
Importar un archivo con extensión .Rdata es lo más simple:
setwd("path_directorio/carpeta/") # No te olvides de indicar en qué directorio se encuentra tu data.
load("tu_data.Rdata") El paquete readr tiene las funciones necesarias para
importar archivos con extensión .csv o
.txt:
library(readr)
setwd("path_directorio/carpeta/")
# Importar data de una archivo .csv
Salarios <- read_csv("salarios.csv")
# Importar data de una archivo .tsv o .txt
Salarios <- read_tsv("salarios.txt")
Salarios <- read_tsv("salarios.tsv")Esta función asume que la primera línea de tu base de datos contiene el nombre de las variables, los valores están separados por una coma o por tabs, y los NAs están representados por celdas vacías.
El paquete `readxl`` tiene las funciones necesarias para importar archivos con extensión .xls o .xlsx:
library(readxl)
setwd("path_directorio/carpeta/")
# Importar data de una archivo .xls o .xlsx
Salarios <- read_excel("salarios.xls",
sheet = "sheet1") # Necesario únicamente si la data esta en una hoja que no sea la primera
Salarios <- read_excel("salarios.xlsx",
sheet = "sheet1")El paquete `readstata13`` tiene las funciones necesarias para importar archivos con extensión .dta:
library(readstata13)
setwd("path_directorio/carpeta/")
# Importar data de una archivo .dta
Salarios <- read.dta13("salarios.dta")Finalmente, el paquete `foreign`` tiene las funciones necesarias para importar archivos con extensión .sav:
Una vez que tienen cargada su base de datos la podemos comenzar a
manipular. En esta sección vamos a ver dos conceptos: wrangling
o la transformación de su base de datos, y el shape de su base
de datos y qué forma es la adecuada para utilizar
ggplot2.
La manipulación y transformación de la base de datos es el primer paso antes de iniciar su visualización. Recuerden, toda nueva variable que ustedes están creando será un elemento más de la historia que quieran contar. Esto no significa que TODAS las variables que creen (eliminen) van a terminar dentro (fuera) de un gráfico. Solo significa que las variables que ustedes creen eventualmente tendrán un propósito, directo o indirecto, en la historia que cuenten.
Si pensamos a las bases de datos como un matriz, la manipulación de la data se la puede hacer en dos dimensiones: filas (observaciones/eje y) o columnas (variables/eje x). Comencemos con la manipulación de las columnas/variables.
La función select del paquete tidyverse nos
permite limitar o seleccionar las variables de nuestra base de
datos.
library(tidyverse)
# Mantener las variables nombre, estatura y género
nueva_data <- vieja_data %>%
select(nombre, estatura, genero)
# Mantener las variables nombre, y todas las que se encuentren entre masa y especie
nueva_data <- vieja_data %>%
select(nombre, masa:especie)
# Mantener todas las variables excepto edad y genero
nueva_data <- vieja_data %>%
select(-edad, -genero)Podemos utilizar colnames para ver el nombre de la
variables y también para renombrar las variables.
## [1] "name" "height" "mass" "hair_color" "skin_color"
## [6] "eye_color" "birth_year" "sex" "gender" "homeworld"
## [11] "species" "films" "vehicles" "starships"
## [1] "nombre" "height" "mass" "hair_color" "skin_color"
## [6] "eye_color" "birth_year" "sex" "gender" "homeworld"
## [11] "species" "films" "vehicles" "starships"
# Renombrar de la primer a la cuarta variable
colnames(starwars)[c(1:4)] <- c("nombre","estatura","masa","color_pelo")
colnames(starwars)## [1] "nombre" "estatura" "masa" "color_pelo" "skin_color"
## [6] "eye_color" "birth_year" "sex" "gender" "homeworld"
## [11] "species" "films" "vehicles" "starships"
También habrán ocasiones en las que quieran eliminar ciertas
filas/observaciones o filas que tengan ciertas características. Para
eliminar observaciones manualmente podemos usar las funciones base de
R. Veamos cómo se ve nuestra base de datos.
## # A tibble: 6 × 14
## nombre estatura masa color_pelo skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Luke S… 172 77 blond fair blue 19 male mascu…
## 2 C-3PO 167 75 <NA> gold yellow 112 none mascu…
## 3 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
## 4 Darth … 202 136 none white yellow 41.9 male mascu…
## 5 Leia O… 150 49 brown light brown 19 fema… femin…
## 6 Owen L… 178 120 brown, gr… light blue 52 male mascu…
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
## # vehicles <list>, starships <list>
Listo. Si quisieran eliminar solo la segunda observación, podrían hacer lo siguiente:
## # A tibble: 6 × 14
## nombre estatura masa color_pelo skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Luke S… 172 77 blond fair blue 19 male mascu…
## 2 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
## 3 Darth … 202 136 none white yellow 41.9 male mascu…
## 4 Leia O… 150 49 brown light brown 19 fema… femin…
## 5 Owen L… 178 120 brown, gr… light blue 52 male mascu…
## 6 Beru W… 165 75 brown light blue 47 fema… femin…
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
## # vehicles <list>, starships <list>
También podemos eliminar de las segunda a la cuarta fila:
# Eliminar manualmente de la segunda a la cuarta observación
starwars_no24 <- starwars[-c(2:4),]
head(starwars_no24)## # A tibble: 6 × 14
## nombre estatura masa color_pelo skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Luke S… 172 77 blond fair blue 19 male mascu…
## 2 Leia O… 150 49 brown light brown 19 fema… femin…
## 3 Owen L… 178 120 brown, gr… light blue 52 male mascu…
## 4 Beru W… 165 75 brown light blue 47 fema… femin…
## 5 R5-D4 97 32 <NA> white, red red NA none mascu…
## 6 Biggs … 183 84 black light brown 24 male mascu…
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
## # vehicles <list>, starships <list>
Por lo general, no es una manera muy práctica de filtrar información.
Una mejor manera es filtrar la información de acuerdo a ciertas
características que encontramos en la propia data. Para eso utilizamos
la función filter.
# Quedarse únicamente con observaciones cuya homeworld sea "Tatooine"
tatooine <- starwars %>%
filter(homeworld=="Tatooine")## filter: removed 77 rows (89%), 10 rows remaining
## # A tibble: 6 × 14
## nombre estatura masa color_pelo skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Luke S… 172 77 blond fair blue 19 male mascu…
## 2 C-3PO 167 75 <NA> gold yellow 112 none mascu…
## 3 Darth … 202 136 none white yellow 41.9 male mascu…
## 4 Owen L… 178 120 brown, gr… light blue 52 male mascu…
## 5 Beru W… 165 75 brown light blue 47 fema… femin…
## 6 R5-D4 97 32 <NA> white, red red NA none mascu…
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
## # vehicles <list>, starships <list>
# Eliminar únicamente observaciones cuya homeworld sea "Tatooine"
no_tatooine <- starwars %>%
filter(homeworld!="Tatooine")## filter: removed 20 rows (23%), 67 rows remaining
## # A tibble: 6 × 14
## nombre estatura masa color_pelo skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
## 2 Leia O… 150 49 brown light brown 19 fema… femin…
## 3 Obi-Wa… 182 77 auburn, w… fair blue-gray 57 male mascu…
## 4 Wilhuf… 180 NA auburn, g… fair blue 64 male mascu…
## 5 Chewba… 228 112 brown unknown blue 200 male mascu…
## 6 Han So… 180 80 brown fair brown 29 male mascu…
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
## # vehicles <list>, starships <list>
La función para crear/modificar variables es mutate.
# Pasar la altura que está en centímetros a pulgadas y la masa que está en kilogramos a libras
newdata <- mutate(starwars,
estatura = estatura * 0.394,
masa = masa * 2.205)
# También podemos crear nuevas variables
newdata <- starwars %>%
mutate(estatura_in = estatura * 0.394,
masa_lb = masa * 2.205)Por lo general, vamos a crear variables basadas en condiciones de otras variables en nuestra data.
# Si la altura es mayor a 180 cm heightcat = "alto", caso contratio heightcat = "chaparro",
newdata <- mutate(starwars,
estaturacat = ifelse(estatura > 180,
"alto",
"chaparro")
# Convertir cualquier color de ojos que nos sea black, blue o brown, en "otro"
newdata <- mutate(starwars,
eye_color = ifelse(eye_color %in% c("black", "blue", "brown"),
eye_color,
"otro")
# Cualquier estatura más que 200 y menos que 75 la pasamos a NA
newdata <- mutate(starwars,
estatura = ifelse(estatura < 75 | estatura > 200,
NA,
estatura)También podemos crear nuevas variables que sean producto de operaciones. Incluso nuevas variables que sean productos de operaciones de datos previamente agrupados.
# Calcular el promedio de estatura de los personajes en la base de datos
starwars %>%
mutate(mean_est = mean(estatura, na.rm = TRUE)) %>%
select(mean_est) ## mutate: new variable 'mean_est' (double) with one unique value and 0% NA
## select: dropped 14 variables (nombre, estatura, masa, color_pelo, skin_color, …)
## # A tibble: 87 × 1
## mean_est
## <dbl>
## 1 175.
## 2 175.
## 3 175.
## 4 175.
## 5 175.
## 6 175.
## 7 175.
## 8 175.
## 9 175.
## 10 175.
## # ℹ 77 more rows
## [1] 174.6049
# Calcular el promedio de estatura por especie:
starwars %>%
group_by(species) %>%
mutate(mean_est = mean(estatura, na.rm = TRUE)) %>%
select(species,mean_est,estatura) ## group_by: one grouping variable (species)
## mutate (grouped): new variable 'mean_est' (double) with 30 unique values and 0% NA
## select: dropped 12 variables (nombre, masa, color_pelo, skin_color, eye_color, …)
## # A tibble: 87 × 3
## # Groups: species [38]
## species mean_est estatura
## <chr> <dbl> <int>
## 1 Human 178 172
## 2 Droid 131. 167
## 3 Droid 131. 96
## 4 Human 178 202
## 5 Human 178 150
## 6 Human 178 178
## 7 Human 178 165
## 8 Droid 131. 97
## 9 Human 178 183
## 10 Human 178 182
## # ℹ 77 more rows
Hay ocasiones en las que no queremos mantener todas esas
observaciones repetidas para esa variable en particular. Para esto,
utilizamos la función distinct.
# Eliminar observaciones repetidas de especie
starwars %>%
group_by(species) %>%
mutate(estatura_ht = mean(estatura, na.rm = TRUE)) %>%
distinct(species, .keep_all=TRUE) %>%
select(species,estatura_ht) ## group_by: one grouping variable (species)
## mutate (grouped): new variable 'estatura_ht' (double) with 30 unique values and 0% NA
## distinct (grouped): removed 49 rows (56%), 38 rows remaining
## select: dropped 13 variables (nombre, estatura, masa, color_pelo, skin_color, …)
## # A tibble: 38 × 2
## # Groups: species [38]
## species estatura_ht
## <chr> <dbl>
## 1 Human 178
## 2 Droid 131.
## 3 Wookiee 231
## 4 Rodian 173
## 5 Hutt 175
## 6 <NA> 175
## 7 Yoda's species 66
## 8 Trandoshan 190
## 9 Mon Calamari 180
## 10 Ewok 88
## # ℹ 28 more rows
Lo primero que deben saber de su data es la unidad de
analásis: la unidad principal a la que se está observando en una
base de datos. Por ejemplo, en la base de datos de
starwars, la unidad de análisis era el personaje. Toda la
información de las variables era sobre el personaje.
## # A tibble: 6 × 14
## nombre estatura masa color_pelo skin_color eye_color birth_year sex gender
## <chr> <int> <dbl> <chr> <chr> <chr> <dbl> <chr> <chr>
## 1 Luke S… 172 77 blond fair blue 19 male mascu…
## 2 C-3PO 167 75 <NA> gold yellow 112 none mascu…
## 3 R2-D2 96 32 <NA> white, bl… red 33 none mascu…
## 4 Darth … 202 136 none white yellow 41.9 male mascu…
## 5 Leia O… 150 49 brown light brown 19 fema… femin…
## 6 Owen L… 178 120 brown, gr… light blue 52 male mascu…
## # ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
## # vehicles <list>, starships <list>
En la siguiente base de datos, la unidad de análisis es el país-año. Es decir, la información de cada variable es sobre un país en un año específico.
## Warning in read.dta13("http://dss.princeton.edu/training/Panel101.dta"):
## Factor codes of type double or float detected in variables
##
## opinion
##
## No labels have been assigned.
## Set option 'nonint.factors = TRUE' to assign labels anyway.
## country year y y_bin x1 x2 x3 opinion op
## 1 A 1990 1342787840 1 0.27790365 -1.1079559 0.28255358 1 1
## 2 A 1991 -1899660544 0 0.32068470 -0.9487200 0.49253848 3 0
## 3 A 1992 -11234363 0 0.36346573 -0.7894840 0.70252335 3 0
## 4 A 1993 2645775360 1 0.24614404 -0.8855330 -0.09439092 3 0
## 5 A 1994 3008334848 1 0.42462304 -0.7297683 0.94613063 3 0
## 6 A 1995 3229574144 1 0.47721413 -0.7232460 1.02968037 1 1
## 7 A 1996 2756754176 1 0.49980500 -0.7815716 1.09228814 3 0
## 8 A 1997 2771810560 1 0.05162839 -0.7048455 1.41590083 1 1
## 9 A 1998 3397338880 1 0.36641079 -0.6983712 1.54872274 3 0
## 10 A 1999 39770336 1 0.39584252 -0.6431540 1.79419804 4 0
## 11 B 1990 -5934699520 0 -0.08184998 1.4251202 0.02342812 2 1
## 12 B 1991 -711623744 0 0.10616001 1.6496018 0.26036251 1 1
## 13 B 1992 -1933116160 0 0.35378519 1.5937191 -0.23439877 2 1
## 14 B 1993 3072741632 1 0.72677696 1.6917576 0.25622433 4 0
## 15 B 1994 3768078848 1 0.71939486 1.7414261 0.41174951 3 0
## 16 B 1995 2837581312 1 0.67154658 1.7083139 0.53584301 4 0
## 17 B 1996 577199360 1 0.81985730 1.5324961 -0.49964902 1 1
## 18 B 1997 1786851584 1 0.88016719 1.5021962 -0.57626772 3 0
## 19 B 1998 -149072048 0 0.70451611 1.4236463 -0.44841924 2 1
## 20 B 1999 -1174480128 0 0.23696731 1.4545859 -0.04936399 4 0
Podemos modificar la unidad de análisis utilizando una de las
técnicas presentadas antes. Por ejemplo, podemos cambiar la unidad de
análisis de nuestra base de datos panel al año:
panel %>%
group_by(year) %>%
mutate(y_mean_year = mean(y, na.rm=TRUE)) %>%
distinct(year,.keep_all=T) %>%
select(year,y_mean_year)## group_by: one grouping variable (year)
## mutate (grouped): new variable 'y_mean_year' (double) with 10 unique values and 0% NA
## distinct (grouped): removed 60 rows (86%), 10 rows remaining
## select: dropped 8 variables (country, y, y_bin, x1, x2, …)
## # A tibble: 10 × 2
## # Groups: year [10]
## year y_mean_year
## <int> <dbl>
## 1 1990 3871104
## 2 1991 621134802.
## 3 1992 650395397.
## 4 1993 3201693936
## 5 1994 3662756754.
## 6 1995 1543687525.
## 7 1996 2416958654.
## 8 1997 3724919991.
## 9 1998 997817328
## 10 1999 1627486320
Lo primero que deben hacer antes de comenzar a evaluar la data, es saber la unidad de análisis. Es decir, de quién estamos hablando cuando vamos a contar nuestra historia.
ggplot2ggplot2 es un paquete que está incluído en
tidyverse y nos permite crear gráficos en capas. Podemos
construir un gráfico complejo comenzando por algo simple e ir agregando
elementos, uno a la vez.
Para construir un gráfico la primera función que necesitas es
ggplot que especifica
mapping) de las variables a propiedades
visuales. El mapping se lo pone dentro de la función
aes (que significa aesthetics).# Carga base de datos
data(CPS85 , package = "mosaicData")
# Especificar la data y el mapeo
CPS85 %>% # Esto es suficiente para especificar la data
ggplot(mapping = aes(x = exper, y = wage))# Lo mismo que antes pero escrito diferente
# ggplot(data = CPS85, mapping = aes(x = exper, y = wage))No hay nada. ¿Por qué? Especificamos que la variable exper debe estar mapeada en el eje x y que la variable wage debe estar en el eje y, pero no hemos especificado qué queremos poner en ese gráfico.
Los geoms son objetos geométricos (puntos, líneas, barras,
etc.) que pueden ser colocados en un gráfico. Se las agrega utilizanso
funciones que comienzan con geom_. En este ejemplo, vamos a
agregar puntos usando la función geom_point y así crearemos
un scatterplot.
En ggplot2, las funciones están enlazadas utilizando un
+ para construir el gráfico final.
¿Quejeso? ¿Un outlier? Pues lo eliminamos:
## filter: removed one row (<1%), 533 rows remaining
Belleza. Una serie de opciones pueden ser modificadas dentro de un
geom_. Estas incluyen color,
size, y alpha. Estos controlan el color, el
tamaño y la transparencia. La transparencia va de 0 (completamente
transparente) a 1 (completamente opaco). La transparencia sirve para
visualizar mejor los elementos que se superponen (y además hace que sus
gráficos se vean bonitos).
CPS85 %>%
filter(wage<30) %>%
ggplot(aes(x = exper, y = wage)) +
geom_point(color = "cornflowerblue",
alpha = .7,
size = 3)## filter: removed one row (<1%), 533 rows remaining
Ahora, podemos agregar la recta de mejor ajuste (best fit
line) utilizando la función geom_smooth. Las opciones
para cada geom_ las pueden consultar utilizando el comando
help(). En el caso de geom_smooth podemos
graficar una línea basada en un modelo lineal (lm).
CPS85 %>%
filter(wage<30) %>%
ggplot(aes(x = exper, y = wage)) +
geom_point(color = "cornflowerblue",
alpha = .7,
size = 3) +
geom_smooth(method = "lm",
color = "grey")## filter: removed one row (<1%), 533 rows remaining
## `geom_smooth()` using formula = 'y ~ x'
Incrementar la experiencia está correlacionado con incrementos en el salario.
Además de mapear las variable al eje x y al eje y, las variables
pueden ser mapeadas a un color, shape,
size, alpha, y otras características visuales
de objetos geométricos. Estos nos permite diferenciar grupos de
observaciones en el mismo gráfico. Agreguemos el género en el gráfico y
representémoslo por color.
CPS85 %>%
filter(wage<30) %>%
ggplot(aes(x = exper,
y = wage,
color = sex)) +
geom_point(alpha = .7,
size = 3) +
geom_smooth(method = "lm",
size = 1.5)## filter: removed one row (<1%), 533 rows remaining
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `geom_smooth()` using formula = 'y ~ x'
La opción de color la ponemos dentro de la funcion
aes porque estamos mapeando la variable a un aesthetic.
Parecería que los hombres tienden a ganar más dinero que las mujeres y que la relación entre experiencia y salario es más fuerte para hombres que para mujeres. Patriarcado que le llaman.
Las scales controlan las características visuales del
gráfico. Las funciones scales_ te permiten modificar el
mapeo.
CPS85 %>%
filter(wage<30) %>%
ggplot(aes(x = exper,
y = wage,
color = sex)) +
geom_point(size = 3) +
geom_smooth(method = "lm",
size = 1.5) +
scale_x_continuous(breaks = seq(0, 60, 10)) +
scale_y_continuous(breaks = seq(0, 30, 5),
label = scales::dollar) +
scale_color_viridis_d(option="E",alpha = 0.5)## filter: removed one row (<1%), 533 rows remaining
## `geom_smooth()` using formula = 'y ~ x'
Ya se ve mejor. Hay unos signos de dólares y los colores están más bonitos. Digamos que queremos ver si la relación de género entre experiencia y salario se mantiene para todos los sectores. Para eso utilizamos…
Facets reproducen el gráfico para cada nivel de una
variable (o algunas variables). En este caso, los gráficos estarán
divididos para cada nivel de la variable sector.
CPS85 %>%
filter(wage<30) %>%
ggplot(aes(x = exper,
y = wage,
color = sex)) +
geom_point(size = 3) +
geom_smooth(method = "lm",
size = 1.5) +
scale_x_continuous(breaks = seq(0, 60, 10)) +
scale_y_continuous(breaks = seq(0, 30, 5),
label = scales::dollar) +
scale_color_viridis_d(option="E",alpha = 0.5) +
facet_wrap(~sector)## filter: removed one row (<1%), 533 rows remaining
## `geom_smooth()` using formula = 'y ~ x'
Un gráfico debe ser fácil de interpretar. El lector no tiene que
estar adivinando cada nombre que se les ocurrió poner a las variables ni
qué significa cada eje ni cada color ni cada movida. La función
labs nos permite cambiar eso.
CPS85 %>%
filter(wage<30) %>%
ggplot(aes(x = exper,
y = wage,
color = sex)) +
geom_point(size = 3) +
geom_smooth(method = "lm",
size = 1.5) +
scale_x_continuous(breaks = seq(0, 60, 10)) +
scale_y_continuous(breaks = seq(0, 30, 5),
label = scales::dollar) +
scale_color_viridis_d(option="E",alpha = 0.5) +
facet_wrap(~sector) +
labs(title = "Relación entre salario y experiencia",
subtitle = "Encuesta de Población",
caption = "fuente: http://mosaic-web.org/",
x = "Años de Experiencia",
y = "Salario por Hora",
color = "Género")## filter: removed one row (<1%), 533 rows remaining
## `geom_smooth()` using formula = 'y ~ x'
Finalmente, le podemos dar los últimos toques para que los gráficos
queden pepa, para que tenga ese je ne sais quoi, es something something.
La funcion theme_ nos permite controlar colores del
background, fuentes, líneas, leyendas, y otros elementos que no están
relacionados a nuestra data. El gráfico final se puede ver algo así.
CPS85 %>%
filter(wage<30) %>%
ggplot(aes(x = exper,
y = wage,
color = sex,
fill = sex)) +
geom_point(size = 3) +
geom_smooth(method = "lm",
size = 1.5) +
scale_x_continuous(breaks = seq(0, 60, 10)) +
scale_y_continuous(breaks = seq(0, 30, 5),
label = scales::dollar) +
scale_color_viridis_d(option="E",alpha = 0.5) +
scale_fill_viridis_d(option="E",alpha = 0.5) +
facet_wrap(~sector) +
labs(title = "Relación entre salario y experiencia",
subtitle = "Encuesta de Población",
caption = "fuente: http://mosaic-web.org/",
x = "Años de Experiencia",
y = "Salario por Hora",
color = "Género",
fill = 'Género') +
theme_minimal() +
theme(legend.position = "bottom") ## filter: removed one row (<1%), 533 rows remaining
## `geom_smooth()` using formula = 'y ~ x'
¡BE-LLE-ZA!
Ahora estamos listos para explorar la data y visualizarla.
Hay gráficos para todo, más de los que lograremos cubrir en el tiempo que tenemos. Los gráficos se adaptan, no solo a lo que quieren mostrar, sino al tipo y la cantidad de variables que quieren incluir. Hay dos tipos de variables que vamos a cubrir:
Vamos a comenzar con gráficos que describen una variable (tanto categórica como continua), y luego pasaremos a gráficos que describen dos. Si bien hay gráficos que describen tres variables, esos no los vamos a cubrir en este taller. Noten que describir una o dos variables no significa que únicamente vamos a utilizar una o dos variables para proporcionar información al gráfico.
Los gráficos univariados muestran la distribución de la data de una sola variable.
La distribución de una sola variable categórica por lo general se la grafica en un gráfico de barras. También en un pie chart, pero esos son horribles y no sirven para nada entonces me niego a enseñarlo.
La base de datos Marriage contiene registros de
matrimonios de 98 individuos de Alabama (unidad de análisis). Primero
vamos a mostras un gráfico de barras que muestra la distribución de los
participantes por signo zodiacal.
library(ggplot2)
data(Marriage, package = "mosaicData")
# Distribución por zodiaco
Marriage %>%
ggplot(aes(x = sign)) +
geom_bar()La mayoría son piscis, obvio.
Podemos modificar el fill y el color de
cada barra, los labels, y el title añadiendo
opciones a la funcion geom_bar.
Marriage %>%
ggplot(aes(x = sign)) +
geom_bar(fill = "cornflowerblue",
color="black",
alpha = .7) +
labs(x = "Zodiaco",
y = "Frecuencia",
title = "Individuos por zodiaco")La barras pueden representar porcentajes en lugar de frecuencias.
Para los gráficos de barras, el código aes(x=sign) es de
hecho un atajo para aes(x = sign, y = ..count..), donde
..count.. es una variable especial que representa la
frecuencia de cada categoría. Podemos calcular los porcentajes y
mapearlos a nuestra variable y. Sin embargo, también debemos cambiar el
geom a geom_col o un geom para graficar columnas
(geom_bar es un subfunción de geom_col).
Marriage %>%
mutate(total_base = n()) %>% # Calcula el total de observaciones de la base
group_by(sign) %>%
mutate(total_signo = n(), # Calcula el total de observaciones por zodiaco
pct_signo = total_signo/total_base) %>% # Calcula el porcentaje
# Como ahora mi unidad de analisis es el signo, entonces tenemos que cambiar la base de datos a esa unidad:
distinct(sign, .keep_all=T) %>%
ggplot(aes(x = sign, y = pct_signo)) +
geom_col(fill = "cornflowerblue",
color="black",
alpha = .7) +
labs(x = "Zodiaco",
y = "Porcentaje",
title = "Individuos por zodiaco") +
# Le decimos que y está en un scale de porcentaje
scale_y_continuous(labels = scales::percent) ## mutate: new variable 'total_base' (integer) with one unique value and 0% NA
## group_by: one grouping variable (sign)
## mutate (grouped): new variable 'total_signo' (integer) with 7 unique values and 0% NA
## new variable 'pct_signo' (double) with 7 unique values and 0% NA
## distinct (grouped): removed 86 rows (88%), 12 rows remaining
Muchas veces van a querer ordenar sus barras por frecuencia (o
porcentaje). Para hacer esto con el gráfico anterior, podemos utilizar
la función reorder y ordenar las categorías por
frecuencia.
Marriage %>%
mutate(total_base = n()) %>% # Calcula el total de observaciones de la base
group_by(sign) %>%
mutate(total_signo = n(), # Calcula el total de observaciones por zodiaco
pct_signo = total_signo/total_base) %>% # Calcula el porcentaje
# Como ahora mi unidad de analisis es el signo, entonces tenemos que cambiar la base de datos a esa unidad:
distinct(sign, .keep_all=T) %>%
# Ordenamos la variabel sign en función de pct_signo
ggplot(aes(x = reorder(sign,pct_signo), y = pct_signo)) +
geom_col(fill = "cornflowerblue",
color="black",
alpha = .7) +
labs(x = "Zodiaco",
y = "Porcentaje",
title = "Individuos por zodiaco") +
# Le decimos que y está en un scale de porcentaje
scale_y_continuous(labels = scales::percent) ## mutate: new variable 'total_base' (integer) with one unique value and 0% NA
## group_by: one grouping variable (sign)
## mutate (grouped): new variable 'total_signo' (integer) with 7 unique values and 0% NA
## new variable 'pct_signo' (double) with 7 unique values and 0% NA
## distinct (grouped): removed 86 rows (88%), 12 rows remaining
o, si quieren que descienda, le ponemos un - a la variable por la cual estamos ordenando:
Marriage %>%
mutate(total_base = n()) %>% # Calcula el total de observaciones de la base
group_by(sign) %>%
mutate(total_signo = n(), # Calcula el total de observaciones por zodiaco
pct_signo = total_signo/total_base) %>% # Calcula el porcentaje
# Como ahora mi unidad de analisis es el signo, entonces tenemos que cambiar la base de datos a esa unidad:
distinct(sign, .keep_all=T) %>%
ggplot(aes(x = reorder(sign,-pct_signo), y = pct_signo)) +
geom_col(fill = "cornflowerblue",
color="black",
alpha = .7) +
labs(x = "Zodiaco",
y = "Porcentaje",
title = "Individuos por zodiaco") +
# Le decimos que y está en un scale de porcentaje
scale_y_continuous(labels = scales::percent) ## mutate: new variable 'total_base' (integer) with one unique value and 0% NA
## group_by: one grouping variable (sign)
## mutate (grouped): new variable 'total_signo' (integer) with 7 unique values and 0% NA
## new variable 'pct_signo' (double) with 7 unique values and 0% NA
## distinct (grouped): removed 86 rows (88%), 12 rows remaining
Si quiren más información, podemos agregar el porcentaje de cada
barra utilizando la función geom_text.
Marriage %>%
# Calcula el total de observaciones de la base
mutate(total_base = n()) %>%
group_by(sign) %>%
# Calcula el total de observaciones por zodiaco
mutate(total_signo = n(),
# Calcula el porcentaje
pct_signo = total_signo/total_base,
# Le damos formato al label
pct_label = paste0(round(pct_signo*100), "%")) %>%
# Como ahora mi unidad de analisis es el signo, entonces tenemos que cambiar la base de datos a esa unidad:
distinct(sign, .keep_all=T) %>%
ggplot(aes(x = reorder(sign,-pct_signo), y = pct_signo)) +
geom_col(fill = "cornflowerblue",
color="black",
alpha = .7) +
labs(x = "Zodiaco",
y = "Porcentaje",
title = "Individuos por zodiaco") +
# Le decimos que y está en un scale de porcentaje
scale_y_continuous(labels = scales::percent) +
# Sacamos los labels de pct_signo. También podemos ajustar la altura del texto on relación a las barras (opcional)
geom_text(aes(label = pct_label),
vjust = -0.25) ## mutate: new variable 'total_base' (integer) with one unique value and 0% NA
## group_by: one grouping variable (sign)
## mutate (grouped): new variable 'total_signo' (integer) with 7 unique values and 0% NA
## new variable 'pct_signo' (double) with 7 unique values and 0% NA
## new variable 'pct_label' (character) with 7 unique values and 0% NA
## distinct (grouped): removed 86 rows (88%), 12 rows remaining
Ya casi. Pero esos nombres de las variables se sobreponen y están feísimos. Tenemos dos soluciones: 1) podemos rotar los ejes utilizando coord_flip o 2) podemos rotar los nombres. Veamos las dos soluciones:
Marriage %>%
# Calcula el total de observaciones de la base
mutate(total_base = n()) %>%
group_by(sign) %>%
# Calcula el total de observaciones por zodiaco
mutate(total_signo = n(),
# Calcula el porcentaje
pct_signo = total_signo/total_base,
# Le damos formato al label
pct_label = paste0(round(pct_signo*100), "%")) %>%
# Como ahora mi unidad de analisis es el signo, entonces tenemos que cambiar la base de datos a esa unidad:
distinct(sign, .keep_all=T) %>%
ggplot(aes(x = reorder(sign,-pct_signo), y = pct_signo)) +
geom_col(fill = "cornflowerblue",
color="black",
alpha = .7) +
labs(x = "Zodiaco",
y = "Porcentaje",
title = "Individuos por zodiaco") +
# Le decimos que y está en un scale de porcentaje
scale_y_continuous(labels = scales::percent) +
# Sacamos los labels de pct_signo. También podemos ajustar la posición horizontal del texto on relación a las barras (opcional)
geom_text(aes(label = pct_label), hjust = -.15) +
# Rotamos los ejes
coord_flip()## mutate: new variable 'total_base' (integer) with one unique value and 0% NA
## group_by: one grouping variable (sign)
## mutate (grouped): new variable 'total_signo' (integer) with 7 unique values and 0% NA
## new variable 'pct_signo' (double) with 7 unique values and 0% NA
## new variable 'pct_label' (character) with 7 unique values and 0% NA
## distinct (grouped): removed 86 rows (88%), 12 rows remaining
Marriage %>%
# Calcula el total de observaciones de la base
mutate(total_base = n()) %>%
group_by(sign) %>%
# Calcula el total de observaciones por zodiaco
mutate(total_signo = n(),
# Calcula el porcentaje
pct_signo = total_signo/total_base,
# Le damos formato al label
pct_label = paste0(round(pct_signo*100), "%")) %>%
# Como ahora mi unidad de analisis es el signo, entonces tenemos que cambiar la base de datos a esa unidad:
distinct(sign, .keep_all=T) %>%
ggplot(aes(x = reorder(sign,-pct_signo), y = pct_signo)) +
geom_col(fill = "cornflowerblue",
color="black",
alpha = .7) +
labs(x = "Zodiaco",
y = "Porcentaje",
title = "Individuos por zodiaco") +
# Le decimos que y está en un scale de porcentaje
scale_y_continuous(labels = scales::percent) +
# Sacamos los labels de pct_signo. También podemos ajustar la posición horizontal del texto on relación a las barras (opcional)
geom_text(aes(label = pct_label),
vjust = -.25) +
# Rotamos el texto el eje x
theme(axis.text.x = element_text(angle = 90,
hjust = 1))## mutate: new variable 'total_base' (integer) with one unique value and 0% NA
## group_by: one grouping variable (sign)
## mutate (grouped): new variable 'total_signo' (integer) with 7 unique values and 0% NA
## new variable 'pct_signo' (double) with 7 unique values and 0% NA
## new variable 'pct_label' (character) with 7 unique values and 0% NA
## distinct (grouped): removed 86 rows (88%), 12 rows remaining
Poniendo junto todo lo que aprendimos antes podemos hacer un gráfico que se ve algo así:
Marriage %>%
# Calcula el total de observaciones de la base
mutate(total_base = n()) %>%
group_by(sign) %>%
# Calcula el total de observaciones por zodiaco
mutate(total_signo = n(),
# Calcula el porcentaje
pct_signo = total_signo/total_base,
# Le damos formato al label
pct_label = paste0(round(pct_signo*100), "%")) %>%
# Como ahora mi unidad de analisis es el signo, entonces tenemos que cambiar la base de datos a esa unidad:
distinct(sign, .keep_all=T) %>%
ggplot(aes(x = reorder(sign,-pct_signo), y = pct_signo)) +
geom_col(fill = "cornflowerblue",
color="black",
alpha = .7) +
# Le decimos que y está en un scale de porcentaje
scale_y_continuous(breaks = seq(0, .2, .05),
labels = scales::percent,
limits = c(0,.2)) +
# Sacamos los labels de pct_signo. También podemos ajustar la posición horizontal del texto on relación a las barras (opcional)
geom_text(aes(label = pct_label),
vjust = -.25) +
theme_minimal() +
# Rotamos el texto el eje x
theme(axis.text.x = element_text(angle = 90,
hjust = 1)) +
labs(title = "Porcentaje de Matrimonios por Zodiaco",
subtitle = "Registro del estado de Alabama",
# caption = "fuente: http://mosaic-web.org/",
x = "",
y = "Porcentaje") ## mutate: new variable 'total_base' (integer) with one unique value and 0% NA
## group_by: one grouping variable (sign)
## mutate (grouped): new variable 'total_signo' (integer) with 7 unique values and 0% NA
## new variable 'pct_signo' (double) with 7 unique values and 0% NA
## new variable 'pct_label' (character) with 7 unique values and 0% NA
## distinct (grouped): removed 86 rows (88%), 12 rows remaining
La distribución de una sola variable continua por lo general se la grafica en un histograma o una gráfico de densidad.
Utilizando la misma base de datos del ejemplo anterior, vamos a graficar el histograma de la edad de los participantes.
Marriage %>%
ggplot(aes(x = age)) +
geom_histogram() +
labs(title = "Participantes por edad",
x = "Edad")## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
La mayoría de los participantes parecen estar en sus veintes y otro grupo en sus cuarenta con uno cuantos rucos rucos al final. Esto es una distribución multimodal.
Podemos modificar los colores utilizanto fill y
color (para el borde).
Marriage %>%
ggplot(aes(x = age)) +
geom_histogram(fill = "peachpuff",
color="peru",
alpha = .7) +
labs(title = "Participantes por edad",
x = "Edad")## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Una de las opciones más importantes de los histogramas son los
bins que controla el número de contenedores en las cuales
la variable númerica está dividida (i.e., el número de barras en el
gráfico). El default es 30, pero es bueno intentar con números más
grandes o más pequeños para tener una mejor representación de la forma
de la distribución.
Marriage %>%
ggplot(aes(x = age)) +
geom_histogram(fill = "peachpuff",
color="peru",
alpha = .7,
bins = 20) +
labs(title = "Participantes por edad",
subtitle = "bins = 20",
x = "Edad")También pueden especificar el binwidth, la ancho de los contenedores representados por las barras.
Marriage %>%
ggplot(aes(x = age)) +
geom_histogram(fill = "peachpuff",
color="peru",
alpha = .7,
binwidth = 3) +
labs(title = "Participantes por edad",
subtitle = "binwidth = 3",
x = "Edad")Una manera alternativa a un histograma es un kernel density plot. La estimación del kernel density es un método no paramétrico para estimar la función de probabilidad de la densidad de una variable continua aleatoria. En fácil, estamos intentando graficar un histograma continuo donde el área bajo la curva es igual a uno.
Al igual que con los histogramas, podemos cambiar el
color y el fill. También podemos modificar el
bw o el término que determina cuánto de la data toma a la
vez para hacer las curvas.
Marriage %>%
ggplot(aes(x = age)) +
geom_density(fill = "peachpuff",
color="peru",
alpha = .7,
bw = 2) +
labs(title = "Participantes por edad",
subtitle = "bandwidth = 2",
x = "Edad") +
theme_minimal()Los gráficos bivariados muestran la relación entre dos variables. El tipo de gráfico dependerá del tipo de variables (categóricas o continuas).
Lo más común para representar la relación entre dos variables categóricas es en gráficos de barras. Por ejemplo, podemos evaluar la relación entre el género y la posición académica en una base de datos que describe los salarios de 397 profesores universitario (2009).
Veamos un stacked bar plot:
Un poco difícil de ver. Podemos ponerlas una junto a la otra.
También podemos hacer que cada barra represente el 100%.
Salaries %>%
ggplot(aes(x = rank, fill = sex)) +
geom_bar(position = "fill") +
labs(y = "Proporción")Este último gráfico es particularmente útil si su objetivo es comparar el porcentaje de una categoría en función de otra variable. Por ejemplo. la proporción de mujeres se reduce a medida que aumenta la posición de Assistant Professor, a Associate Professor, a Professor.
Como en todo, podemos personalizar y embellecer los gráficos.
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
group_by(rank) %>%
mutate(total_rank= n()) %>%
group_by(rank,sex) %>%
mutate(total_rank_sex = n(),
pct_rank_sex = total_rank_sex/total_rank,
lbl = scales::percent(pct_rank_sex)) %>%
ungroup() %>%
ggplot(aes(x = rank,fill = sex)) +
geom_bar(position = "fill") +
scale_y_continuous(breaks = seq(0, 1, .2),
labels = scales::percent) +
scale_fill_viridis_d(option="E",alpha = 0.5) +
labs(x = "Posición",
fill = "Género",
title = "Posición por Género",
y = "") +
theme_minimal()## mutate: changed 397 values (100%) of 'rank' (0 new NA)
## group_by: one grouping variable (rank)
## mutate (grouped): new variable 'total_rank' (integer) with 3 unique values and 0% NA
## group_by: 2 grouping variables (rank, sex)
## mutate (grouped): new variable 'total_rank_sex' (integer) with 6 unique values and 0% NA
## new variable 'pct_rank_sex' (double) with 6 unique values and 0% NA
## new variable 'lbl' (character) with 4 unique values and 0% NA
## ungroup: no grouping variables
La relación entre dos variables continuas por lo general es representada por un scatter plot o por una gráfico de línea. Comencemos por el primero.
El scatter plot es un gráfico donde se representa cada vairable en un
eje. Por ejemplo, podemos utilizar la misma base de datos anterior y
graficar experiencia (yrs.since.phd) vs. salario académico
(salary) para profesores universitarios.
las optiones de geom_point pueden ser cambiadas:
color: color del puntosize: tamaño del puntoshape: forma del puntoalpha : transparencia del puntoComo aprendimos antes, las funciones scale_x_continuous
y scale_y_continuous controlan las escalas del eje x y el
eje y.
Como siempre, usamos estas funciones para hacer gráficos más atractivos.
# scatterplot simple
Salaries %>%
ggplot(aes(x = yrs.since.phd,
y = salary)) +
geom_point(color="peachpuff",
size = 2,
alpha=.7) +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009") +
theme_minimal()Podemos resumir la relación que se muestra en el scatterplot utilizando un best fit line. Hay varias maneras de hacer la línea (de representar la relación): lineal, polinomial, o no paramétrica (loess). Por default, el intervalo de confianza mostrado es del 95%.
# scatterplot lineal
Salaries %>%
ggplot(aes(x = yrs.since.phd,
y = salary)) +
geom_point(color="peachpuff",
size = 2,
alpha=.7) +
geom_smooth(method = "lm",
color = "peru") +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009") +
theme_minimal()## `geom_smooth()` using formula = 'y ~ x'
Claramente, a medida que aumenta la experiencia también aumenta el salario. Sin embargo, parece que hay un caída justo al final. Los profesores con mucha experiencia tienen menores salarios. Una línea recta no captura este efecto no-lineal. Una curva captura mejor este efecto.
Un polinomio dibuja una curva con la siguiente forma:
\[\hat{y} = \beta_0 + \beta_1x + \beta_2x^2 + \beta_3x^3+...\]
Por lo general, una fórmula cuadrática (una curva) o cúbica (dos curvas) se utiliza. Rara vez se utiliza una curva de un polinomio de orden mayor a 3. Utilizando una fórmula cuadrática obtenemos el siguiente gráfico:
# scatterplot cuadrático
Salaries %>%
ggplot(aes(x = yrs.since.phd,
y = salary)) +
geom_point(color="peachpuff",
size = 2,
alpha=.7) +
geom_smooth(method = "lm",
formula = y ~ poly(x, 2),
color = "peru") +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009") +
theme_minimal()Cuando una de las dos variables representa tiempo, un gráfico de
línea puede ser una manera efectiva de mostrar la relación. Por ejemplo,
el código a continuación muestra la relación entre tiempo
(year) y el promedio de expectativa de vida
(lifeExp_mean) en las Amerícas entre 1952 y 2007. La data
viene de la base de datos gapminder.
data(gapminder, package="gapminder")
# obtenemos el promedio de vida en las Américas
americas <- gapminder %>%
group_by(continent, year) %>%
mutate(lifeExp_mean = mean(lifeExp)) %>%
filter(continent == "Americas") %>%
distinct(continent, year, .keep_all=T) %>%
ungroup()## group_by: 2 grouping variables (continent, year)
## mutate (grouped): new variable 'lifeExp_mean' (double) with 60 unique values and 0% NA
## filter (grouped): removed 1,404 rows (82%), 300 rows remaining
## distinct (grouped): removed 288 rows (96%), 12 rows remaining
## ungroup: no grouping variables
# simple grafico de línea
americas %>%
ggplot(aes(x = year,
y = lifeExp_mean)) +
geom_point(color="peachpuff",
size = 3,
alpha=.8) +
geom_line(color="peru",
size = 1,
alpha=.6) +
labs(x = "Año",
y = "Expectativa de Vida",
title = "Cambios en la Expectativa de Vida a lo Largo del Tiempo",
subtitle = "Las Américas (1952-2007)") +
theme_minimal()Cuando queremos graficar la relación entre una variable categórica y una continua, hay varios tipos de gráficos disponibles. Entre estos están los gráficos de barras, los density plots, box plots, etc. Veamos algunas opciones.
En la sección de Gráficos de Barras los utilizamos para mostrar el número de casos de una variable o de dos variables. También se pueden utilizar gráficos de barras para mostrar estadísticas básicas (por ej., promedio o mediana) de una variable continua para cada nivel de una variable categórica.
Por ejemplo, el siguiente gráfico muestra el salario promedio de profesores universitarios en cada posición académica.
##
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
##
## discard
## The following object is masked from 'package:readr':
##
## col_factor
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
# calcular promedios
group_by(rank) %>%
mutate(mean_salary = mean(salary)) %>%
distinct(rank, .keep_all=T) %>%
# graficar
ggplot(aes(x = rank,
y = mean_salary)) +
geom_bar(stat = "identity",
fill = "peachpuff",
color = "peru",
alpha = .75) +
geom_text(aes(label = dollar(mean_salary)),
vjust = -0.3) +
scale_y_continuous(breaks = seq(0, 130000, 20000),
label = dollar) +
labs(title = "Salario Promedio por Posicion",
subtitle = "Salario de 9 meses entre 2008 y 2009",
x = "",
y = "") +
theme_minimal()## mutate: changed 397 values (100%) of 'rank' (0 new NA)
## group_by: one grouping variable (rank)
## mutate (grouped): new variable 'mean_salary' (double) with 3 unique values and 0% NA
## distinct (grouped): removed 394 rows (99%), 3 rows remaining
Un problema con este tipo de gráficos es que no muestran la distribucion de la data. El siguiente gráfico trata de corregir esa limitación.
Hay varias maneras de utilizar los gráficos de densidad para comparar distribuciones. Aquí hay tres.
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
# calcular promedios
group_by(rank) %>%
mutate(mean_salary = mean(salary)) %>%
# graficar
ggplot(aes(x = salary,
fill = rank,
color = rank)) +
geom_density() +
# Dibujar las líneas verticales
geom_vline(aes(xintercept = mean_salary, color = rank), linetype="dashed") +
scale_fill_viridis_d(option="E",alpha = 0.5) +
scale_color_viridis_d(option="E",alpha = 0.85) +
labs(title = "Distribución de Salario por Posición",
subtitle = "Salario de 9 meses entre 2008 y 2009",
x = "Salario",
y = "" ,
fill = "Posición",
color = "Posición") +
theme_minimal()## mutate: changed 397 values (100%) of 'rank' (0 new NA)
## group_by: one grouping variable (rank)
## mutate (grouped): new variable 'mean_salary' (double) with 3 unique values and 0% NA
facets:Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
# calcular promedios
group_by(rank) %>%
mutate(mean_salary = mean(salary)) %>%
# graficar
ggplot(aes(x = salary,
fill = rank,
color = rank)) +
geom_density() +
# Dibujar las líneas verticales
geom_vline(aes(xintercept = mean_salary, color = rank), linetype="dashed") +
# Facets
facet_wrap(~rank, ncol = 1) +
scale_fill_viridis_d(option="E",alpha = 0.5) +
scale_color_viridis_d(option="E",alpha = 0.85) +
labs(title = "Distribución de Salario por Posición",
subtitle = "Salario de 9 meses entre 2008 y 2009",
x = "Salario",
y = "" ) +
theme_minimal() +
theme(legend.position = "none")## mutate: changed 397 values (100%) of 'rank' (0 new NA)
## group_by: one grouping variable (rank)
## mutate (grouped): new variable 'mean_salary' (double) with 3 unique values and 0% NA
facets:library(ggridges)
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
# calcular promedios
group_by(rank) %>%
mutate(mean_salary = mean(salary)) %>%
# graficar
ggplot(aes(x = salary,
y = rank,
fill = rank,
color = rank)) +
geom_density_ridges() +
# Dibujar las líneas verticales
geom_vline(aes(xintercept = mean_salary, color = rank), linetype="dashed") +
# Facets
scale_fill_viridis_d(option="E",alpha = 0.5) +
scale_color_viridis_d(option="E",alpha = 0.85) +
labs(title = "Distribución de Salario por Posición",
subtitle = "Salario de 9 meses entre 2008 y 2009",
x = "Salario",
y = "" ) +
theme_minimal() +
theme(legend.position = "none")## mutate: changed 397 values (100%) of 'rank' (0 new NA)
## group_by: one grouping variable (rank)
## mutate (grouped): new variable 'mean_salary' (double) with 3 unique values and 0% NA
## Picking joint bandwidth of 5590
Un box plot muestra el percentil 25to, la media y el percentil 75to de uns distribución. Los bigotes (whiskers o líneas verticales) caputuran el 99% de una distribución normal, y la observaciones que están fuera de ese rango se grafica como puntos que representan outliers (ver la Figura de abajo).
Si ponemos dos box plot uno al lado del otro, estos resultan muy útiles para comparar grupos en una variabla continua.
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
# graficar
ggplot(aes(y = salary,
x = rank,
fill = rank,
color = rank)) +
# dibujar boxplots
geom_boxplot() +
scale_y_continuous(breaks = seq(0, 300000, 50000),
label = dollar) +
scale_fill_viridis_d(option="E",alpha = 0.5) +
scale_color_viridis_d(option="E",alpha = 0.85) +
labs(title = "Distribución de Salario por Posición",
subtitle = "Salario de 9 meses entre 2008 y 2009",
x = "Posición",
y = "" ) +
theme_minimal() +
theme(legend.position = "none")## mutate: changed 397 values (100%) of 'rank' (0 new NA)
Una alternativa que combina el box plot con el density distribution es el violin plot (es un plot de densidad rotado 90 grados y duplicado como espejo).
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
# graficar
ggplot(aes(y = salary,
x = rank,
fill = rank,
color = rank)) +
# dibujar boxplots
geom_violin() +
scale_y_continuous(breaks = seq(0, 300000, 50000),
label = dollar) +
scale_fill_viridis_d(option="E",alpha = 0.5) +
scale_color_viridis_d(option="E",alpha = 0.85) +
labs(title = "Distribución de Salario por Posición",
subtitle = "Salario de 9 meses entre 2008 y 2009",
x = "Posición",
y = "" ) +
theme_minimal() +
theme(legend.position = "none")## mutate: changed 397 values (100%) of 'rank' (0 new NA)
O, mejor aún, pueden combinar el box plot con el violin plot:
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
# graficar
ggplot(aes(y = salary,
x = rank,
fill = rank,
color = rank)) +
# dibujar boxplots
geom_violin() +
geom_boxplot(width = .15,
outlier.size = 2) +
scale_y_continuous(breaks = seq(0, 300000, 50000),
label = dollar) +
scale_fill_viridis_d(option="E",alpha = 0.5) +
scale_color_viridis_d(option="E",alpha = 0.85) +
labs(title = "Distribución de Salario por Posición",
subtitle = "Salario de 9 meses entre 2008 y 2009",
x = "Posición",
y = "" ) +
theme_minimal() +
theme(legend.position = "none")## mutate: changed 397 values (100%) of 'rank' (0 new NA)
Para graficar la relación entre tres o más variables (aunque ya tres es mucho), podemos utilizar dos herramientas que ya usamos previamente: grouping y faceting.
Podemos retomar el scatter plot que hicimos antes y agregarle una categoría más:
# scatterplot por categoria
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
ggplot(aes(x = yrs.since.phd,
y = salary,
color = rank)) +
geom_point(size = 2) +
scale_color_viridis_d(option="E",alpha = 0.85) +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009",
color = "Posición") +
theme_minimal()## mutate: changed 397 values (100%) of 'rank' (0 new NA)
Ahora, podemos incluso agregar una dimensión más (género) modificando la forma de cada punto:
# scatterplot por categoria
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
ggplot(aes(x = yrs.since.phd,
y = salary,
color = rank,
shape = sex)) +
geom_point(size = 2) +
scale_color_viridis_d(option="E",alpha = 0.85) +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009",
color = "Posición",
shape = "Género") +
theme_minimal()## mutate: changed 397 values (100%) of 'rank' (0 new NA)
O podemos usar otra dimensión para resaltar el eje x o el eje y. Por ejemplo, podemos resaltar el salario:
# scatterplot por categoria resaltando salario
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
ggplot(aes(x = yrs.since.phd,
y = salary,
color = rank,
size = salary)) +
geom_point() +
scale_color_viridis_d(option="E",alpha = 0.65) +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009",
color = "Posición",
size = "Salario") +
theme_minimal()## mutate: changed 397 values (100%) of 'rank' (0 new NA)
Y resaltando el eje x (experiencia) se vería así:
# scatterplot por categoria resaltando salario
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
ggplot(aes(x = yrs.since.phd,
y = salary,
color = rank,
size = yrs.since.phd)) +
geom_point() +
scale_color_viridis_d(option="E",alpha = 0.5) +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009",
color = "Posición",
size = "Experiencia") +
theme_minimal()## mutate: changed 397 values (100%) of 'rank' (0 new NA)
Finalmente, podemos hacer las misma best fit lines, pero esta vez por categoría.
# scatterplot por categoria
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
ggplot(aes(x = yrs.since.phd,
y = salary,
color = rank,
fill = rank)) +
geom_point(size = 2) +
geom_smooth(method = "lm",
formula = y ~ poly(x, 2)) +
scale_color_viridis_d(option="E",alpha = 0.5) +
scale_fill_viridis_d(option="E",alpha = 0.65) +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009",
color = "Posición",
fill = "Posición") +
theme_minimal()## mutate: changed 397 values (100%) of 'rank' (0 new NA)
Mientras grouping te permite visualizar todas las dimensiones y categorías en un solo gráfico, con faceting puedes dividir en subgráficos tu data. Cada subgráfico estará limitado a la categoría que elijas. Por ejemplo, podemos replicar el gráfico anterior, pero en lugar de hacer grouping por posición, podemos hacer faceting por posición.
# scatterplot por categoria
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
ggplot(aes(x = yrs.since.phd,
y = salary,
# Mantengo esto por estética
color = rank,
fill = rank)) +
geom_point(size = 2) +
geom_smooth(method = "lm",
formula = y ~ poly(x, 2)) +
scale_color_viridis_d(option="E",alpha = 0.5) +
scale_fill_viridis_d(option="E",alpha = 0.65) +
scale_y_continuous(label = scales::dollar,
limits = c(50000, 250000)) +
scale_x_continuous(breaks = seq(0, 60, 10),
limits=c(0, 60)) +
facet_wrap(~ rank, ncol = 1) +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009",
color = "Posición",
fill = "Posición") +
theme_minimal() +
theme(legend.position = "none")## mutate: changed 397 values (100%) of 'rank' (0 new NA)
Podemos agregar facets por más de una categoría. El gráfico anterior le podemos agregar un facet de género también.
# scatterplot por categoria
Salaries %>%
mutate(rank = recode_factor(rank,
AsstProf = "Assistant Professor",
AssocProf = "Associate Professor",
Prof = "Professor")) %>%
ggplot(aes(x = yrs.since.phd,
y = salary,
# Mantengo esto por estética
color = rank,
fill = rank)) +
geom_point(size = 2) +
# Cambiamos a lineal porque hay menos data por facet
geom_smooth(method = "lm",
formula = y ~ poly(x, 1)) +
scale_color_viridis_d(option="E",alpha = 0.5) +
scale_fill_viridis_d(option="E",alpha = 0.65) +
scale_y_continuous(label = scales::dollar) +
# Agrego scales = "free" para que se muevan libremente los ejes.
facet_wrap(sex ~ rank, scales = "free") +
labs(x = "Años desde el PhD",
y = "",
title = "Experiencia vs. Salario",
subtitle = "Salario de 9 meses entre 2008 y 2009",
color = "Posición",
fill = "Posición") +
theme_minimal() +
theme(legend.position = "none")## mutate: changed 397 values (100%) of 'rank' (0 new NA)
Finalmente, vamos a regrear a nuestra línea de tiempo y, en lugar de hacer un solo gráfico de la expectativa de vida para todo el continente, podemos hacer uno por país, utilizando facets:
gapminder %>%
filter(continent == "Americas") %>%
ggplot(aes(x=year, y = lifeExp)) +
geom_line(color="peachpuff") +
geom_point(color="peru") +
facet_wrap(~country) +
theme_minimal(base_size = 9) +
theme(axis.text.x = element_text(angle = 45,
hjust = 1)) +
labs(title = "Evolución de la Expectativa de Vida",
x = "Año",
y = "Expectativa de Vida") ## filter: removed 1,404 rows (82%), 300 rows remaining
Podemos observar como en todo los países la expectativa de vida sube, salvo en El Salvador entre 1970 y 1990.
¿Hay más gráficos que se pueden hacer? Los hay. Pero la única manera de saber qué gráficos necesita su historia es trabajando y conociendo su data. Recuerden que la visualización de la data tiene que comunicar un punto de manera efectiva. Demasiada información, y el lector no sabrá qué priorizar en el gráfico. Muy poco información, y el lector no sabrá sobre qué es el gráfico. El barroco es enemigo de la visualización de datos:
Finalmente, cualquier pregunta que puedan tener acerca de la visualización de datos, seguramente pueden encontrar la respuesta en el internet. Ahora sí, vayan al mundo y grafiquen!!